/*
	SimpleMan - Simple Download Manager
	Copyright (C) 2007 Roque Pinel, Rio de Janeiro
	Email simplemandownloader@gmail.com
*/
/*
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <iostream>
#include <fstream>
#include <time.h>

#include <unistd.h>

#include "sm_util.h"
#include "sm_ftp.h"

#define MAXRECV        1024


SM_FTP::SM_FTP ( )
{
		/* setting */
	start = true;
}


	/* initial functions */

const bool SM_FTP::configure ( const std::string &host, const int &port )
{
	if( ! SM_Socket::create() )
	{
		std::cerr << "Error: could not create client socket." << std::endl;
		return false;
	}

	if( ! SM_Socket::connect( host, port ) )
	{
		std::cerr << "Error: could not bind to port." << std::endl;
		return false;
	}

	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return <= 0 )
	{
		return false;
	}

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "CONNECT: " << buf << std::endl;
#endif /* DEBUG */

	if( buf[0] != '2' || buf[1] != '2' || buf[2] != '0' )
	{
		return false;
	}
	
	return true;
}

const bool SM_FTP::user_conf ( const std::string &user, const std::string &pass )
{
	if( ! SM_Socket::send( "USER " + user + "\r\n" ) )
		return false;
	
	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return <= 0 )
		return false;

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "USER: " << buf << std::endl;
#endif /* DEBUG */

	if( buf[0] != '3' || buf[1] != '3' || buf[2] != '1' )
		return false;

	if( ! SM_Socket::send( "PASS " + pass + "\r\n" ) )
		return false;

	std::string temp = "";
	recv_return = SM_Socket::recv ( buf, sizeof( buf ) );

	while( recv_return > 0 )
	{
		if(recv_return <= MAXRECV )
			buf[recv_return] = '\0';
		temp.append( buf );
		recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	}

#ifdef DEBUG
	std::cout << "PASS: " << temp << std::endl;
#endif /* DEBUG */

	if( temp[0] != '2' || temp[1] != '3' || temp[2] != '0' )
		return false;

	return true;
}

const bool SM_FTP::pasv_mode ( std::string &ip, int &port )
{
	if( ! SM_Socket::send( "PASV\r\n" ) )
		return false;
	
	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return <= 0 )
		return false;

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "PASV: " << buf << std::endl;
#endif /* DEBUG */

	if( buf[0] != '2' || buf[1] != '2' || buf[2] != '7' )
		return false;

	std::string ret_cod = buf;
	std::string ip_port = "";
	unsigned int posA = ret_cod.find("(");
	unsigned int posB = ret_cod.find(")");

	if( posA == std::string::npos || posB == std::string::npos )
		return false;
	posA++;
	ret_cod = ret_cod.substr( posA, posB - posA );

#ifdef DEBUG
	std::cout << "PASV ret_cod: " << ret_cod << std::endl;
#endif /* DEBUG */

	int n = 0, p = 0;
	char token[6][6];
	for( unsigned int i = 0; i < ret_cod.length(); i++ )
	{
		if( ret_cod[i] == ',' )
		{
			token[n][p] = '\0';
			n++;
			p = 0;
		}
		else
		{
			token[n][p] = ret_cod[i];
			p++;
		}
	}
	token[n][p] = '\0';

	for( int i = 0; i < 4; i++ )
	{
		ip.append( token[i] );
		if( i < 3 )
			ip.append( "." );
	}
	port = atoi( token[4] ) * 256 + atoi( token[5] );

#ifdef DEBUG
	std::cout << "PASV ip: " << ip << " port: " << port << std::endl;
#endif /* DEBUG */

	return true;
}

SM_Socket * SM_FTP::open_pasv ( const std::string &host, const int &port )
{
	SM_Socket *pasv_socket = new SM_Socket();

	if( ! pasv_socket->create() )
	{
		std::cerr << "Could not create client socket." << std::endl;
		return NULL;
	}

	if( ! pasv_socket->connect( host, port ) )
	{
		std::cerr << "Could not bind to port." << std::endl;
		return NULL;
	}

	return pasv_socket;
}

const bool SM_FTP::rest ( const int &position )
{
	if( ! SM_Socket::send( "REST " + SM_Util::itoa( position ) + "\r\n" ) )
		return false;

	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return <= 0 )
		return -1;

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "REST: " << buf << std::endl;
#endif /* DEBUG */

	if( buf[0] != '3' || buf[1] != '5' || buf[2] != '0' )
		return false;

	return true;
}

const bool SM_FTP::retr ( const std::string &fileName )
{
	if( ! SM_Socket::send( "RETR " + fileName + "\r\n" ) )
		return false;

	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return <= 0 )
		return -1;

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "RETR: " << buf << std::endl;
#endif /* DEBUG */

	if( buf[0] != '1' || buf[1] != '5' || buf[2] != '0' )
		return false;

	return true;
}


	/* common functions */

const std::string SM_FTP::socket_msg ( const std::string &msg )
{
	if( ! SM_Socket::send( msg ) )
		return "";
	std::string temp = "";
	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );

	while( recv_return > 0 )
	{
puts("OPA 1");
		recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
puts("OPA 2");
		if(recv_return <= MAXRECV )
			buf[recv_return] = '\0';
		temp.append( buf );
std::cout << "RETURN: " << recv_return << std::endl;
	}

	return temp;
}


	/* set functions */

const bool SM_FTP::set_type  ( const bool &binary )
{
	std::string ftp_code;

	if( binary )
		ftp_code = "TYPE I\r\n";
	else
		ftp_code = "TYPE A\r\n";

	if( ! SM_Socket::send( ftp_code ) )
		return false;
	
	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return <= 0 )
		return false;

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "TYPE: " << buf << std::endl;
#endif /* DEBUG */

	if( buf[0] != '2' || buf[1] != '0' || buf[2] != '0' )
		return false;

	return true;
}

const bool SM_FTP::set_stop ( void )
{
	if( ! start )
		return false;
	start = false;
	return true;
}

const bool SM_FTP::set_speedLimit ( const int &new_speedLimit )
{
	sm_speedLimit = new_speedLimit;

	return true;
}


	/* get functions */

const long  SM_FTP::get_size  ( const std::string & fileName )
{
	if( ! SM_Socket::send( "SIZE " + fileName + "\r\n" ) )
		return -1;
	
	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return <= 0 )
		return -1;

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "SIZE: " << buf << std::endl;
#endif /* DEBUG */

	if( buf[0] != '2' || buf[1] != '1' || buf[2] != '3' )
		return -1;

	long size = atoi( ( std::string( buf ).substr( 4, recv_return - 4 ) ).c_str() );

#ifdef DEBUG
	std::cout << "size: " << size << std::endl;
#endif /* DEBUG */

	return size;
}

const bool SM_FTP::get_file ( SM_TASK_DATA *ptr_taskData, const std::string fileName, const std::string filePath, const int bytes, const bool newFile )
{
	std::string pasv_ip;
	int pasv_port;

	if( ! pasv_mode( pasv_ip, pasv_port ) )
		return false;

	SM_Socket *pasv_socket = open_pasv( pasv_ip, pasv_port );

	if( ! pasv_socket )
		return false;

	if( ! rest( bytes ) )
		return false;

	if( ! retr( fileName ) )
		return false;

	std::ofstream *outFile;
		
	if( newFile )
		outFile = new std::ofstream( filePath.c_str(), std::ios::trunc | std::ios::binary );
	else
		outFile = new std::ofstream( filePath.c_str(), std::ios::app | std::ios::binary );

	if( ! outFile )
	{
		std::cerr << "Could not open file to write." << std::endl;
		delete pasv_socket;
		return false;
	}

	time_t before, after;
	time( &before );

	char buf[MAXRECV + 1];
	int recv_return = pasv_socket->recv( buf, MAXRECV );
	if ( recv_return == -1 )
	{
		std::cerr << "Could not receive from server." << std::endl;
		delete pasv_socket;
		return false;
	}

	bool option = false;
	int i;
	for( i = 0; i < recv_return - 3; i++ )
	{
		if( buf[i] == '\r' && buf[i+1] == '\n' && buf[i+2] == '\r' && buf[i+3] == '\n' )
		{			
			i += 4;
			option = true;
			break;
		}
	}

	int percentage, total_recv = 0, time_pas, time_lef, speed;
	total_recv += recv_return;

		/* refresh some columns */
	ptr_taskData->completed += recv_return - i;
	time( &after );
	time_pas = after - before;
	if( time_pas == 0 )
		speed = 0;
	else
		speed =  total_recv / time_pas;
	if( speed == 0 )
		time_lef = 0;
	else
		time_lef = ( ptr_taskData->size - ptr_taskData->completed ) / speed;
	ptr_taskData->speed = speed / 1024;
	if( time_lef >= 0 )
		ptr_taskData->time = SM_Util::show_time(time_lef);
	if( ptr_taskData->completed == 0 || ptr_taskData->size == 0 )
		percentage = 0;
	else
		percentage = ( ( ( double ) ptr_taskData->completed ) / ptr_taskData->size ) * 100;
	if(percentage <= 100)
		ptr_taskData->percentage = percentage;
	else
		ptr_taskData->percentage = 100;

	while ( recv_return > 0 && start )
	{
		if( option )
		{
			outFile->write( buf+i, recv_return-i );
			option = false;
		}
		else
			outFile->write( buf, recv_return );

		active_speedLimit( speed, total_recv, after - before );

		recv_return = pasv_socket->recv( buf, MAXRECV );
		if( recv_return == -1 )
		{
			std::cerr << "Could not receive from server." << std::endl;
			delete pasv_socket;
			return false;
		}
		total_recv += recv_return;

			/* refresh some columns */
		ptr_taskData->completed += recv_return;
		time( &after );
		time_pas = after - before;
		if( time_pas == 0 )
			speed = 0;
		else
			speed =  total_recv / time_pas;
		if( speed == 0 )
			time_lef = 0;
		else
			time_lef = ( ptr_taskData->size - ptr_taskData->completed ) / speed;
		ptr_taskData->speed = speed / 1024;

		if( time_lef >= 0 )
			ptr_taskData->time = SM_Util::show_time( time_lef );

		percentage = ( ( ( double ) ptr_taskData->completed ) / ptr_taskData->size ) * 100;
		if( percentage <= 100 )
			ptr_taskData->percentage = percentage;
		else
			ptr_taskData->percentage = 100;
	}
	outFile->close();	
	delete outFile;
	delete pasv_socket;

	recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return <= 0 )
		return false;

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "RETR RECV: " << buf << std::endl;
#endif /* DEBUG */

		/* 226 -> successed */ 
	if( buf[0] == '2' && buf[1] == '2' && buf[2] == '6' )
	{
		if( ptr_taskData->size > 0 )
			ptr_taskData->completed = ptr_taskData->size;
		return true;
	}
		/* 426 -> aborted */
	else if( buf[0] == '4' && buf[1] == '2' && buf[2] == '6' )
		return true;	

	return false;
}


	/* final functions */

const bool SM_FTP::quit ( void )
{
	if( ! SM_Socket::send( "QUIT\r\n" ) )
		return false;
	
	char buf[MAXRECV + 1];
	int recv_return = SM_Socket::recv ( buf, sizeof( buf ) );
	if( recv_return == -1 || recv_return == 0 )
		return false;

#ifdef DEBUG
	if(recv_return <= MAXRECV )
		buf[recv_return] = '\0';
	std::cout << "QUIT: " << buf << std::endl;
#endif /* DEBUG */

	if( buf[0] != '2' || buf[1] != '2' || buf[2] != '1' )
		return false;

	return true;
}


	/* active functions */

const bool SM_FTP::active_speedLimit ( const int &speed, const int &total_recv, const int &time_pass )
{
	if( sm_speedLimit == 0 || speed <= ( sm_speedLimit * 1024 ) )
		return true;

	int delay = ( float ) total_recv / sm_speedLimit;

	if( delay > time_pass )
		usleep( delay );

	return true;
}


/* EOF */

